/****************************************************
 *         FileName : lexical_analyzer.c
 *         Function : Lexical Analysis
 *         Author : TonyZhu
 *         Time : 2011.04.25
 *****************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "keyword.h"

FILE *fp_input = NULL;
FILE *fp_output = NULL;

char buffer[MAXBUFFER];
char idvalue[MAXBUFFER];
char digitvalue[MAXBUFFER];
char stringvalue[MAXBUFFER];
char _table[MAXBUFFER][MAX_IDENTIFER_LINE];

int _state;
int stringindex;
int _line_num;
int _table_index;
int _num_type;



int check_filename(char * str);
void lex_analyser(int i);
void analysis_process(char *file_name);
void init(char * file_name);
void pre_process(char ch);
void lex();

int iskey();
int isnum(int input);
int isothers(int input);
void num_print(int start, int end, int _isfloat);

//后续需要设置参数opt

int main(int argc, char* argv[]){
/*	if(argc < 2){
		printf("Please Add Source Code File Name\n");		
		printf("For Example : lex_analyzer test.gda\n");
		return 0;
	}

	while(argc > 1){
		argc --;
		if(check_filename(argv[argc])){
			continue;
		}else{
			analysis_process(argv[argc]);
		}	
	}
*/
	analysis_process("gda_test.gda");
	return 0;
}

int check_filename(char * str){
	char *p = strrchr(str,'.');
	if(p == NULL){
		printf("Can't indentify the file\n");
		return 1;
	}else{

		if(strcmp(p, extension_name) != 0){
			printf("No goAda Language File\n");
			return 1;
		}
	}
	return 0;
}

void analysis_process(char * file_name){
	printf("Start Processing\n");
	init(file_name);
	lex();	
}

void init(char * filename){
	_state = STATE_NORMAL;//状态初始化
	char ch;
	if((fp_input = fopen(filename,"rb")) == NULL){
		fprintf(stderr, "can't open %s", filename);
		exit(1);
	}
	if((fp_output = fopen("go_ada.tmp","wb")) == NULL){
		fprintf(stderr, "can't create temp file\n");
		exit(1);
	}//预处理,处理注释
	while((ch = fgetc(fp_input)) != EOF){
		pre_process(ch);
	}

	fclose(fp_input);
	fclose(fp_output);
}
//可以考虑加入宏，或者不加呢？
void pre_process(char ch){
	switch(_state){
		case STATE_NORMAL:
			if(ch == '#'){
				_state = STATE_NOTE;
			}else{
				if (ch == '"'){
					_state = STATE_STRING;
				}
				fputc(ch,fp_output);
			}
			break;
		case STATE_NOTE:
			if(ch == '#'){
				_state = STATE_LINE_NOTE;
			}else{
				_state = STATE_BLOCK_NOTE;
			}
			break;
		case STATE_LINE_NOTE:
			if(ch == '\n'){
				fputc(ch,fp_output);
				_state = STATE_NORMAL;
			}
			break;
		case STATE_BLOCK_NOTE:
			if(ch == '#'){
				_state = STATE_NORMAL;
			}
			break;
		case STATE_STRING:
			if(ch == '"'){
				_state = STATE_NORMAL;
			}
			fputc(ch,fp_output);
			break;
		default:
			break;
	}
}
	//在无注释的代码中处理符号标
void lex(){
	if((fp_input = fopen("go_ada.tmp","rb")) == NULL){
		fprintf(stderr, "Open temp file goada.tmp error\n");
		exit(1);
	}
	if((fp_output = fopen("go_ada.out","wb")) == NULL){
		fprintf(stderr, "can't create table file\n");
		exit(1);
	}
	char ch; 
	int i = 0;
	_line_num = 1;
	while((ch = fgetc(fp_input)) != EOF){
		if(ch != '\n'){
			buffer[i++] = ch;
		} else {
			buffer[i++] = '\0';
			if(i == 1) ; 
			else {
				lex_analyser(i);			
			}
			i = 0;
			++ _line_num;
		}
	}
	fclose(fp_input);
	fclose(fp_output);
	
	if((fp_output = fopen("goada.table","wb")) == NULL){
        fprintf(stderr, "TABLE File open failed!\n");
        printf("Hello World\n");
        exit(1);
    }
    for(i = 0; i < _table_index; ++i){
        fprintf(fp_output, "%d\t%s\n", i, _table[i]);
    }
    fclose(fp_output);
}

void lex_analyser(int end){
	int i = 0;
    int idindex = 0;
    int keyindex;

    while(buffer[i] != '\0'){
		if(_state == STATE_STRING){
			while(buffer[i] != '\0'){
				if(buffer[i] == '\\'){
		            stringvalue[stringindex++] = buffer[++i];            
		            i++;
		        } else if(buffer[i] == '\''){
		            stringvalue[stringindex] = '\0';
		            fprintf(fp_output, "%s\t%s\n", "STRING", stringvalue);
		            fprintf(fp_output, "%s\t%s\n", "Q_MARK", "\'");
		            _state = STATE_NORMAL;
		            ++i;
		        }
		        else{
		            stringvalue[stringindex++] = buffer[i++];
		        }
			}
			if (buffer[i] == '\0'){			
				fprintf(stderr, "Line %d: You Can not write the string in two line\n", _line_num);
				exit(1);
			}
		}
		if(isalpha(buffer[i])){
            idindex = 0;
            for(; i < end && isalnum(buffer[i]); ++i){
                idvalue[idindex++] = buffer[i];
            }
            idvalue[idindex] = '\0';
            if((keyindex = iskey()) != -1){
                fprintf(fp_output, "%s\t%s\n", _keywordDefine[keyindex], _keyword[keyindex]);
            }
            else{
                fprintf(fp_output, "%s\t%s\n", "ID", idvalue);
                int temptableindex = 0;
                for(; temptableindex < _table_index; ++temptableindex){
                    if(strcmp(idvalue, _table[temptableindex]) == 0){
                        break;
                    }
                }
                if(temptableindex == _table_index){
                    strcpy(_table[_table_index++], idvalue);
                }
            }
        }
        else if(isdigit(buffer[i])){
            i = isnum(i);
        }
        else if(buffer[i] == '\0'){
           ;;
        }
        else{
            if(buffer[i] == '\''){
                fprintf(fp_output, "%s\t%s\n", "Q_MARK", "\'");
                if(i > 0){         
                    if(buffer[i - 1] == '\\'){
                    }
                    else if( _state == STATE_NORMAL){
                        _state = STATE_STRING;
                        stringindex = 0;              
                    }
                    else if(_state == STATE_STRING){
                        _state = STATE_NORMAL;
                    }
                    else{
                        fprintf(stderr, "Line %d: string state error in \"%s\"\n",_line_num,  buffer);
                    }
                }
                ++i;
            }
            else if(buffer[i] == '\n' || buffer[i] == '\r' || buffer[i] == '\t' || buffer[i] == ' '){
                ++i;    
            }
            else{
                i = isothers(i);
            }
        }
    }
}

int iskey(){
    int i = 0;
    for(i = 0; i < NUMOFKEYWORD; ++i){
        if(strcmp(_keyword[i], idvalue) == 0){
            return i;
        }
    }
    return -1;
}

int isothers(int input){
    int i = input;
    int index;

    switch(buffer[i]){          
        case '<':
            if(buffer[i + 1] == '='){
                fprintf(fp_output, "%s\t%s\n", "LE", "<=");
                return (i + 2);
            }
            break;
        case '>':
            if(buffer[i + 1] == '='){
                fprintf(stderr, "%s\t%s\n", "GE", ">=");
                return (i + 2);
            }
            break;
        case '=':
        	if(buffer[i+1] == '='){
                fprintf(stderr, "%s\t%s\n", "EQ", "==");
                return (i + 2);
            }else{
            	return (i + 1);
            }
            break;
        case '!':
            if(buffer[i + 1] == '='){
                fprintf(stderr, "%s\t%s\n", "NEQ", "1=");
                return (i + 2);
            }
            break;
        case '\\':                  
            return (i + 1);
            break;
        default:
            break;
    }
    char temp[10];
    temp[0] = buffer[input];
    temp[1] = '\0';
    for(index = 0; index < NUMOFOPERATOR; ++index){
        if(strcmp(temp, _operator[index]) == 0){
            fprintf(fp_output, "%s\t%s\n", _operatorDefine[index], _operator[index]);
            return (input + 1);
        }
    }
    for(index = 0; index < NUMOFSEPARATOR; ++index){
        if(strcmp(temp, _separator[index]) == 0){
            fprintf(fp_output, "%s\t%s\n", _separatorDefine[index], _separator[index]);
            return (input + 1);
        }
    }
    fprintf(stderr, "Line %d: Unkown Symbol :\"%s\"\n",_line_num, temp);
    exit(1);
    return 1;
}

void num_print(int start, int end, int _isfloat){
    int i = start;
    int a = 0;

    for(; i < end; ++i){
        digitvalue[a++] = buffer[i];
    }
    digitvalue[a] = '\0';
    if(_isfloat){
        fprintf(fp_output, "%s\t%s\n", "REAL", digitvalue);
    }
    else{
        fprintf(fp_output, "%s\t%s\n", "INT", digitvalue);
    }
}

int isnum(int input){
	int i  = input;
    int _num_type = NUM_TYPE_DEC;
    int _dotnum = 0;

    while(isalnum(buffer[i]) && buffer[i] != '\0'){
        if(buffer[i++] == '0'){
            if(buffer[i++] == 'x'){
                _num_type = NUM_TYPE_HEX;           
            }
            else{
                i--;
                _num_type = NUM_TYPE_OCT;           
            }
        }
        else{
            _num_type = NUM_TYPE_DEC;             
        }
        switch(_num_type ){
            case NUM_TYPE_HEX:
                while((buffer[i] == '.' || isdigit(buffer[i]) || (buffer[i] >= 'a' && buffer[i] <= 'f') || (buffer[i] >= 'A' && buffer[i] <= 'F')) && buffer[i] != '\0'){
                    if(buffer[i] == '.' && !_dotnum){
                        _dotnum  = 1;
                    }
                    else if(buffer[i] == '.' && _dotnum){         
                        fprintf(stderr, "Line %d: There should only one '.' in HEX_Number :\"%s\"\n",_line_num, buffer);
                        exit(1);
                    }
                    ++i;
                }

                if(isalpha(buffer[i])){ 
                    fprintf(stderr, "Line %d, HEX_Number Format Error in \"%s\"\n", _line_num, buffer);
                    exit(1);
                }

                num_print(input, i, _dotnum);
                return i;
                break;
            case NUM_TYPE_OCT:
                while((buffer[i] == '.' || (buffer[i] >= '0' && buffer[i] <= '7')) && buffer[i] != '\0'){
                    if(buffer[i] == '.' && !_dotnum){
                        _dotnum = 1;
                    }
                    else if(buffer[i] == '.' && _dotnum){
                        fprintf(stderr, "Line %d: There should only one '.' in OCT_Number :\"%s\"\n",_line_num, buffer);
                        exit(1);
                    }
                    ++i;
                }
                if(isalpha(buffer[i]) || (buffer[i] > '7' && buffer[i] <= '9')){
                    fprintf(stderr, "Line %d: OCT_Number Format Error in \"%s\"\n",_line_num, buffer);
                    exit(1);
                }
                num_print(input, i, _dotnum);
                return i;
                break;
            case NUM_TYPE_DEC:
                while((buffer[i] == '.' || isdigit(buffer[i])) && buffer[i] != '\0'){
                    if(buffer[i] == '.' && !_dotnum){
                        _dotnum = 1;
                    }
                    else if(buffer[i] == '.' && _dotnum){
                        fprintf(stderr, "Line %d: There should only one '.' in DEC_Number :\"%s\"\n",_line_num, buffer);
                        exit(1);
                    }
                    ++i;
                }
                if(isalpha(buffer[i])){
                    fprintf(stderr, "Line %d: DEC_Number Format Error in \"%s\"\n",_line_num, buffer);
                    exit(1);
                }
                num_print(input, i, _dotnum);
                return i;
                break;
            default:
                fprintf(stderr,"Line %d: Unkown Error in \"%s\"\n",_line_num, buffer);
                exit(1);
                break;
        }
    }
    
}
